package com.example.demo02.aop;

import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.system.SystemUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.example.demo02.annotation.Decrypt;
import com.example.demo02.annotation.Encrypt;
import com.example.demo02.annotation.SkipDecrypt;
import com.example.demo02.common.BaseRequest;
import com.example.demo02.config.SunlandConfig;
import com.example.demo02.constants.AttributeConstant;
import com.example.demo02.entity.Person;
import com.example.demo02.exception.InvalidParameterException;
import com.example.demo02.exception.ServiceAssert;
import com.example.demo02.exception.ServiceException;
import com.example.demo02.utils.*;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import ma.glasnost.orika.MapperFacade;
import org.apache.commons.codec.binary.Base64;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.lang.reflect.Method;
import java.text.Annotation;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Set;

@Slf4j
@Aspect
@Component
public class SafetyAspect {
    public static final Validator validator= Validation.buildDefaultValidatorFactory().getValidator();
    @Autowired
    MapperFacade mapperFacade;

    /**
     * Pointcut 切入点
     * 匹配
     * cn.huanzi.qch.baseadmin.sys.*.controller、
     * cn.huanzi.qch.baseadmin.*.controller包下面的所有方法
     */
    @Pointcut(value = "execution(public * com.example.demo02.controller..*.*(..))")
    public void safetyAspect() {}

    /**
     * 环绕通知
     */
    @Around(value = "safetyAspect()")
    @SneakyThrows
    public Object around(ProceedingJoinPoint pjp)  {
        //获取切入点方法
        Method method = ((MethodSignature) pjp.getSignature()).getMethod();
        //获取http请求类型(外面来的，不是controller方法标注的)
        String methodName = Opt.ofNullable((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                .map(ServletRequestAttributes::getRequest).map(HttpServletRequest::getMethod).get();
        HttpMethod httpMethod = HttpMethod.resolve(methodName);
        //接口参数
        Object[] args = pjp.getArgs();
        //入参解密
        decryptArguments(args,method,httpMethod);
        //加了@valid注解的属性校验
        validateSpecificArguments(args);
        //执行方法
        return pjp.proceed(args);
    }

    private void validateSpecificArguments(Object[] args) {
        for (int i = 0; i < args.length; i++) {
            Object argument=args[i];
            if(HelpUtil.isProjectRequestBody(argument)){
                Set<ConstraintViolation<Object>> violations = validator.validate(argument);
                ConstraintViolation<Object> violation = CollUtil.getFirst(violations);
                String violationMessage = Opt.ofNullable(violation).map(ConstraintViolation::getMessage).get();
                ServiceAssert.isTrue(StrUtil.isBlank(violationMessage),()->new InvalidParameterException(violationMessage));
            }
        }
    }

    private void decryptArguments(Object[] args, Method method, HttpMethod httpMethod) {
        //没配置默认不解密
        boolean isDecrypt= SystemUtil.get("sunland.common.is_decrypt")==null?false:Boolean.TRUE.toString().equals(SystemUtil.get("sunland.common.is_decrypt"));
        //接口无参数、配置没开解密、请求方法不是post --不解密
        if(!isDecrypt || ArrayUtil.isEmpty(args) || !HttpMethod.POST.equals(httpMethod)){
            return;
        }
        //方法上加了skipDecrypt注解
        if(AnnotationUtil.hasAnnotation(method, SkipDecrypt.class)){
            return;
        }
        //入参解密
        for (int i = 0; i < args.length; i++) {
            //是不是项目的请求体，一些httpRequest，response之类的参数不参与加密，只加密前端传的
            Object argument=args[i];
            if(HelpUtil.isProjectRequestBody(argument)){
                Opt<BaseRequest> baseRequestOpt = Opt.ofTry(() -> mapperFacade.map(argument, BaseRequest.class));
                String data=HelpUtil.decryptData(baseRequestOpt);
                args[i]=JSON.parseObject(data,argument.getClass());
            }
        }
    }



}
